/* reclaim.c - reclaim, freemem */

#include <xinu.h>
#include <stdio.h>

syscall	freemem(char *, uint32);

/*------------------------------------------------------------------------
 *  reclaim  -  Take a process ID as an argument and free all the heap memory 			allocated by that process
 *------------------------------------------------------------------------
 */
syscall	reclaim(
		pid32 pid	/* pid of the process being terminated	*/
	)
{
	intmask	mask;			/* saved interrupt mask		*/
	struct 	heapblk	*hprev, *hcurr, *htemp, *sharedblk;
	char	*hblkaddr;		/* ptr to block to be freed	*/
	struct	procent *prptr;		/* ptr to process' table entry	*/
	int32 i;
	
	mask = disable();
	if (isbadpid(pid)) {
		restore(mask);
		return SYSERR;
	}

	/* Stop sharing other processes' blocks				*/
	prptr = &proctab[pid];
	for(i = 0; i < NSHBLK; i++)
	{
		if(prptr->shheap[i] != NULL) {
			sharedblk = (struct heapblk *)(prptr->shheap[i]);
			sharedblk->sharec --;
		}
	}

	/* Take the blocks allocated by process pid off the heap list	*/
	hprev = &heaplist;
	hcurr = heaplist.hnext;
	while (hcurr != NULL) {
		/*	block is allocated by pid			*/
		if (hcurr->hpid == pid) {
			hcurr->sharec --;
			if (hcurr->sharec == 0) {/* check if shared	*/
				/* Take the block off the heap list	*/
				hprev->hnext = hcurr->hnext;
				heaplist.hlength -= hcurr->hlength;
				printf("Process (id%d) free memory %d bytes\r\n", pid, hcurr->hlength);
				/* Put the block back in the free list	*/
				hblkaddr = (char *)hcurr;
				htemp = hcurr;
				hcurr = hcurr->hnext;
				freemem(hblkaddr, htemp->hlength);
			} else {
				printf("Process (id%d) cannot free memory %d bytes because it's shared!\r\n", pid, hcurr->hlength);
				hprev = hcurr;
				hcurr = hcurr->hnext;
				}
		/*	processes share the process all terminate */
		} else if (hcurr->sharec == 0) {
			/* Take the block off the heap list */
			hprev->hnext = hcurr->hnext;
			heaplist.hlength -= hcurr->hlength;
			printf("Shared memory %d bytes originally allocated by process (id%d) is freed because processes sharing it all terminate \r\n", hcurr->hlength, hcurr->hpid);
			/* Put the block back in the free list */
			hblkaddr = (char *)hcurr;
			htemp = hcurr;
			hcurr = hcurr->hnext;
			freemem(hblkaddr, htemp->hlength);
		} else {		
			hprev = hcurr;
			hcurr = hcurr->hnext;
		}
	}
	restore(mask);
	return OK;
}
